#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "t_types.h"
#include "t_error.h"
#include "common/console.h"
#include "common/cond.h"

typedef struct _T_COND_OBJ_S
{
	pthread_cond_t stCond;
	pthread_mutex_t stLock;
} T_COND_OBJ_S;

T_COND_HANDLE CondCreate(void)
{
	T_COND_OBJ_S *pCondOjb = NULL;

	pCondOjb = (T_COND_OBJ_S *)malloc(sizeof(T_COND_OBJ_S));
	if (pCondOjb == NULL)
	{
		console_error("can not malloc for cond object!\n");
		return NULL;
	}
	memset((void *)pCondOjb, 0, sizeof(T_COND_OBJ_S));

	if (pthread_cond_init(&pCondOjb->stCond, NULL) != 0)
	{
		console_error("pthread_cond_init in Cond module error!\n");
		goto _ERROR;
	}

	if (pthread_mutex_init(&pCondOjb->stLock, NULL) != 0)
	{
		console_error("pthread_mutex_init in Cond module error!\n");
		goto _ERROR;
	}

	return (T_COND_HANDLE)pCondOjb;

_ERROR:
	if (pCondOjb)
	{
		free(pCondOjb);
	}
	return NULL;
}

int CondDestroy(T_COND_HANDLE hCond)
{
	T_COND_OBJ_S *pCondOjb = (T_COND_OBJ_S *)hCond;

	if (pCondOjb == NULL)
	{
		console_error("invalid arguments!\n");
		return T_ERR_INVALID_ARG;
	}

	/////////////pthread_mutex_lock(&pCondOjb->stLock);
	pthread_cond_signal(&pCondOjb->stCond);
	////////////pthread_mutex_unlock(&pCondOjb->stLock);

	pthread_cond_destroy(&pCondOjb->stCond);
	pthread_mutex_destroy(&pCondOjb->stLock);

	free((void *)hCond);

	return T_ERR_OK;
}

int CondSignal(T_COND_HANDLE hCond)
{
	T_COND_OBJ_S *pCondOjb = (T_COND_OBJ_S *)hCond;
	int nRet = T_ERR_OK;

	if (pCondOjb == NULL)
	{
		console_error("invalid arguments!\n");
		return T_ERR_INVALID_ARG;
	}

	////////////pthread_mutex_lock(&pCondOjb->stLock);

	if (pthread_cond_signal(&pCondOjb->stCond) != 0)
	{
		console_error("Cond signal error!\n");
		nRet = T_ERR_ERROR;
	}
	///////////pthread_mutex_unlock(&pCondOjb->stLock);

	return nRet;
}

int CondWait(T_COND_HANDLE hCond)
{
	T_COND_OBJ_S *pCondOjb = (T_COND_OBJ_S *)hCond;
	int nRet = T_ERR_OK;

	if (pCondOjb == NULL)
	{
		console_error("invalid arguments!\n");
		return T_ERR_INVALID_ARG;
	}

	pthread_mutex_lock(&pCondOjb->stLock);

	if (pthread_cond_wait(&pCondOjb->stCond, &pCondOjb->stLock) != 0)
	{
		console_error("Cond wait error!\n");
		nRet = T_ERR_ERROR;
	}
	pthread_mutex_unlock(&pCondOjb->stLock);

	return nRet;
}

int CondTimeWait(T_COND_HANDLE hCond, int nMillisecond)
{
	T_COND_OBJ_S *pCondOjb = (T_COND_OBJ_S *)hCond;
	int nRet = T_ERR_OK;
	struct timespec abstime;

	if (pCondOjb == NULL || nMillisecond < 0)
	{
		console_error("invalid arguments!\n");
		return T_ERR_INVALID_ARG;
	}

	memset((void *)&abstime, 0, sizeof(abstime));
	if (clock_gettime(CLOCK_REALTIME, &abstime) != 0)
	{
		console_error("clock_gettime error!\n");
		return T_ERR_ERROR;
	}

	abstime.tv_sec = nMillisecond / 1000;
	abstime.tv_nsec = (nMillisecond % 1000) * 1000000;

	pthread_mutex_lock(&pCondOjb->stLock);

	if (pthread_cond_timedwait(&pCondOjb->stCond, &pCondOjb->stLock, &abstime) != 0)
	{
		console_error("Cond time wait error!\n");
		nRet = T_ERR_ERROR;
	}
	pthread_mutex_unlock(&pCondOjb->stLock);

	return nRet;
}

int CondWaitLock(T_COND_HANDLE hCond, pthread_mutex_t *pMutex)
{
	T_COND_OBJ_S *pCondOjb = (T_COND_OBJ_S *)hCond;
	int nRet = T_ERR_OK;

	if (pCondOjb == NULL || pMutex == NULL)
	{
		console_error("invalid arguments!\n");
		return T_ERR_INVALID_ARG;
	}

	if (pthread_cond_wait(&pCondOjb->stCond, pMutex) != 0)
	{
		console_error("Cond wait error!\n");
		nRet = T_ERR_ERROR;
	}

	return nRet;
}

int CondTimeWaitLock(T_COND_HANDLE hCond, int nMillisecond, pthread_mutex_t *pMutex)
{
	T_COND_OBJ_S *pCondOjb = (T_COND_OBJ_S *)hCond;
	int nRet = T_ERR_OK;
	struct timespec abstime;

	if (pCondOjb == NULL || nMillisecond < 0 || pMutex == NULL)
	{
		console_error("invalid arguments!\n");
		return T_ERR_INVALID_ARG;
	}

	memset((void *)&abstime, 0, sizeof(abstime));
	if (clock_gettime(CLOCK_REALTIME, &abstime) != 0)
	{
		console_error("clock_gettime error!\n");
		return T_ERR_ERROR;
	}

	abstime.tv_sec = nMillisecond / 1000;
	abstime.tv_nsec = (nMillisecond % 1000) * 1000000;

	if (pthread_cond_timedwait(&pCondOjb->stCond, pMutex, &abstime) != 0)
	{
		console_error("Cond time wait error!\n");
		nRet = T_ERR_ERROR;
	}

	return nRet;
}

#if 0

int sum;
D_COND_HANDLE gshCond;
pthread_mutex_t gsMutex;

void* dec(void *argv)
{
    while(sum==0)
    {
        D_DBG("dec waiting\n");
#if 1		
		CondWait(gshCond);
D_DBG("dec Wakeup!\n");

#else
		pthread_mutex_lock(&gsMutex);
		CondWaitLock(gshCond, &gsMutex);
		pthread_mutex_unlock(&gsMutex);
		D_DBG("dec Wakeup!\n");

#endif
    }    
    D_DBG("dec, sum=%d\n",sum);
	D_WARN("dec thread exit!\n");
    pthread_exit(NULL);
    return 0;
}

void* add(void *argv)
{
    if(sum==0)
    {
        D_DBG("add signal\n");
        CondSignal(gshCond);    
    }    
    sum++;
	D_DBG("add sum=%d\n",sum);
    sleep(1);
	CondSignal(gshCond); 
    sum++;
    D_DBG("add sum=%d\n",sum);
	D_WARN("add thread exit!\n");
    pthread_exit(NULL);
    return 0;    
}

int main(int argc,char** argv)
{
    pthread_t ptid1,ptid2;
    sum = 0;

	pthread_mutex_init(&gsMutex, NULL);

	gshCond = CondCreate();
	if(gshCond == NULL)
	{
		D_ERR("CondCreate error\n");
		return -1;
	}
    
    pthread_create(&ptid1,NULL,dec,NULL);
	sleep(1);
    pthread_create(&ptid2,NULL,add,NULL);
    
    
    int counter = 0;  
  	while(counter != 10)
  	{  
    	D_DBG("counter: %d\n", counter);  
    	sleep(1);  
    	counter++;  
  	}  

	CondDestroy(gshCond);
	pthread_mutex_destroy(&gsMutex);
    
    return 0;    
}

#endif
